﻿using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Sockets;
using RemoteControl.Localization;

namespace RemoteControl
{
    public class SendCommandCompletedEventArgs : EventArgs
    {
        public bool Success { get; internal set; }
        public string ErrMessage { get; internal set; }
    }

    public class Client
    {
        const int ActionSize = 32;
        const int BufferSize = 1024;

        public event EventHandler<SendCommandCompletedEventArgs> SendCommandCompleted;

        private void OnSendCommandCompleted(SendCommandCompletedEventArgs e)
        {
            EventHandler<SendCommandCompletedEventArgs> handler = SendCommandCompleted;
            if (handler != null) handler(this, e);
        }

        public Client()
        {

        }

        public void SendCommandAsync(EndPoint endPoint, string action, string command)
        {
            var actionBuffer = Encoding.UTF8.GetBytes(action);
            var contextBuffer = Encoding.UTF8.GetBytes(command);

            var bufferSize = contextBuffer.Length + ActionSize + 4;
            var lengthBuffer = BitConverter.GetBytes(contextBuffer.Length);

            var buffer = new byte[BufferSize];
            int offset = 0;
            Array.Copy(lengthBuffer, 0, buffer, offset, lengthBuffer.Length);
            offset += 4;
            Array.Clear(buffer, offset, ActionSize);
            Array.Copy(actionBuffer, 0, buffer, offset, actionBuffer.Length);
            offset += ActionSize;
            Array.Copy(contextBuffer, 0, buffer, offset, contextBuffer.Length);

            var socketAsyncEventArgs = new SocketAsyncEventArgs
                                           {
                                               UserToken =
                                                   new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                                                              ProtocolType.Tcp)
                                           };
            socketAsyncEventArgs.Completed += AsyncCompleted;
            socketAsyncEventArgs.SetBuffer(buffer, 0, bufferSize);
            socketAsyncEventArgs.RemoteEndPoint = endPoint;
            ProcessConnect(socketAsyncEventArgs);
        }

        #region Private
        private void ProcessConnect(SocketAsyncEventArgs e)
        {
            var socket = (Socket)e.UserToken;
            bool willRaiseEvent = socket.ConnectAsync(e);
            if (!willRaiseEvent)
            {
                ConnectCompleted(e);
            }
        }

        private void ProcessSend(SocketAsyncEventArgs e)
        {
            var socket = (Socket)e.UserToken;
            bool willRaiseEvent = socket.SendAsync(e);
            if (!willRaiseEvent)
            {
                SendCompleted(e);
            }
        }

        private void AsyncCompleted(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    ConnectCompleted(e);
                    break;
                case SocketAsyncOperation.Send:
                    SendCompleted(e);
                    break;
                case SocketAsyncOperation.Receive:
                    ReceiveCompleted(e);
                    break;
                default:
                    return;
            }

        }

        private void ReceiveCompleted(SocketAsyncEventArgs e)
        {
        }
        private void ConnectCompleted(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                ProcessSend(e);
            }
            else
            {
                OnSendCommandCompleted(new SendCommandCompletedEventArgs { Success = false, ErrMessage = AppResources.Could_not_connect });
            }
        }

        private void SendCompleted(SocketAsyncEventArgs e)
        {
            var socket = (Socket)e.UserToken;
            socket.Close();
            if (e.SocketError == SocketError.Success)
            {
                OnSendCommandCompleted(new SendCommandCompletedEventArgs { Success = true });
            }
            else
            {
                OnSendCommandCompleted(new SendCommandCompletedEventArgs { Success = false, ErrMessage = AppResources.Send_command_fail });
            }
            e.Dispose();

        }
        #endregion

    }
}
